iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
自我挑戰組

入坑 RoR 必讀 - Ruby 物件導向設計實踐系列 第 18

Day18 CH9 設計節省成本的測試(上)

  • 分享至 

  • xImage
  •  

談測試之前,我們必須先知道,撰寫可修改的程式碼實踐依賴於三項不同的技能:

  • 必須瞭解物件導向設計
    了解如何設計良好的對象和類結構是至關重要的。合理的設計可以最小化代碼修改的成本,使代碼易於維護和擴展。

  • 必須熟練於程式碼重構
    代碼重構是一種改進代碼內部結構的過程,而不改變其外部行為。通過小而確切的步驟來調整代碼,逐步將一種設計轉變為另一種設計。
    良好的設計應該能夠以最小的代價保留最大的靈活性,延遲設計決策,並將承諾推遲到有更具體的需求時。

  • 撰寫高價值測試的能力
    編寫有效的測試是非常重要的,因為它們可以為重構提供信心。有效的測試可以證明修改後的代碼仍然能夠正確執行,而不會增加整體成本。好的測試應該能夠經受住代碼重構的考驗,它們不會因代碼修改而需要重寫,並且為該功能背書,確保重構的正確性並且不會損壞功能。

設計良好的程式碼必須易於修改,重構時是將一種設計修改為另一種,而測試則可以讓你在重構時確保功能不會損壞。

具有意圖的測試

為什麼我們需要寫測試?測試最常見的觀點包括:

  • 減少錯誤: 測試可以幫助捕獲和減少代碼中的錯誤和缺陷,從而提高應用程序的質量。
  • 提供文件記錄: 測試案例充當了程式碼行為的文件,使其他開發人員能夠理解代碼的預期行為。
  • 改進應用程序設計: 編寫測試通常需要考慮程式碼的結構和設計,這可以促使開發人員編寫更模塊化、可維護和可擴展的代碼。

如同設計的真正目的,測試的真正目的是要降低成本。大原則是如果撰寫、維護和執行測試所消耗的時間,比修復錯誤、撰寫文件和設計應用程式所需要的時間還要多,那麼很顯然這些測試並不值得撰寫。

瞭解測試的意圖

1. 找出錯誤
在開發過程中,盡早找出錯誤或缺陷能讓將來的設計選項更有彈性,調整上相對容易,此外,隨著程式碼的累積,嵌入的錯誤會產生依賴。如果在後期才修復這些錯誤,可能需要大量修改依賴程式碼,盡早修復錯誤總是能降低成本。

2. 提供文件
當程式碼有許多人共同開發,或是已經經過好幾手開發人員時,測試文件提供了程式碼行為的文件,使其他開發人員能夠理解代碼的預期行為。

3. 延後設計決定

  • 填補坑洞: 在開發過程中,可能會遇到需要進一步設計但還沒有足夠信息的情況。這些“坑洞”通常需要等待更多信息,同時抗拒提交特定設計的壓力。測試可以幫助你標記這些待修改點,使其成為具體但可變的部分。
    - 具體案例轉抽象: 有時你可能只有一個具體案例,但相信未來會有更多案例出現,可以使用更好的代碼來處理它們並將它們抽象化。然而,你現在還不知道這個抽像是什麼。依賴於測試和接口使你可以在不影響底層代碼的情況下進行重構。
    - 依賴於介面: 測試可以驗證介面的行為是否保持良好,同時確保對底層代碼的修改不會破壞這些測試。這使你能夠安全地進行設計決策的延遲。

4. 支持抽象

  • 逐步提升抽象層次:當你獲得更多資訊時,你可以逐步提高代碼的抽象層次來做出更多設計決策。測試的存在使得這個過程更加安全,因為它們可以驗證抽象的正確性。
  • 依賴於小而獨立的對象:良好的設計通常會依賴於小而獨立的對象,這些對象可以相互協作。隨著抽象的增加,這些小對象之間的交互變得更為複雜,但它們仍然是設計的基本元素。
  • 抽象的靈活性和代價:抽象提供了設計的靈活性,但也伴隨著一定的複雜性代價。隨著程式碼的增長,測試變得更加必要,因為它們可以記錄每個抽象的介面,提供安全的修改和擴展代碼的能力。

5. 暴露出設計缺陷
如果編寫一個測試需要復雜的設置,那麼這意味著代碼對上下文的依賴過於復雜;如果測試一個對象需要涉及到許多其他對象,那麼這表示代碼具有大量的依賴關係;當設計很糟糕時,測試會難以編寫,其他開發人員也可能發現這段代碼難以重用。

測試雖然可以幫助暴露底層代碼的設計問題,但需要確保測試本身也具有良好的設計。最佳實踐是為重要的部分編寫鬆散耦合的測試,以最低的成本獲得測試的好處。

瞭解測試的內容

  • 避免過多的測試與重複內容:確保測試數量適度,只編寫必要的測試。(此處可以根據開發時程來做判定,若時間許可,以補齊測試案例為原則)。刪除測試中的重複內容,以減少修改測試的成本,確保測試套件中沒有不必要的冗餘。
  • 將測試放置在正確的地方:測試應該覆蓋核心功能和邊界情況,以確保只有在絕對必要時才需要修改它們。
  • 理解黑盒測試:將面向對象應用程序視為一系列黑盒之間傳遞消息的系統。每個對像都應該像一個黑盒一樣,只能通過預定義的消息來訪問,而不需要關心內部實現細節。
  • 強調對象邊界:設計良好的對象應該有堅固的邊界,只通過有限的消息來與外部互動。這種方法讓你能夠設計出易於修改的應用程序,並編寫成本最低的測試。
  • 遵守設計原則:應用程序中實施的設計原則同樣適用於測試。測試應該遵循設計原則以保持鬆散耦合和穩定的介面。
  • 測試類型:測試應該分為兩項,狀態測試行為測試 。狀態測試關注消息回傳的狀態,通常是使用結果來驗證預期值。行為測試關注消息是否被正確傳遞,通常涉及到命令消息的測試。
  • 命令消息 vs. 查詢消息:命令消息是具有副作用的消息,測試應該驗證它們是否被正確傳遞。查詢消息是不帶副作用的消息,測試不需要驗證它們。

瞭解測試的時機

優先撰寫測試可以迫使少量的可重複使用性從一開始就被建構在物件,不過,優先撰寫測試既不能取代也不能保證一支設計良好的應用程式。對於新手設計師來說,保持對測試價值的信心很重要。在正確的時間,採取正確的數量,測試並撰寫測試優先的程式碼,將會是我們的首要課題。

瞭解測試的方法

  • 何種測試風格?

    • 測試驅動開發 (Test Driven Development, 簡稱為 TDD ):

      由內向外的方式,它通常是從領域物件開始測試,然後在程式碼鄰接層的測試裡重複使用這些新建立的領域物件。

    • 行為驅動開發(Behavior Driven Development, 簡稱爲BDD ):

      由外向内的方式,它會在應用程式的邊界上建立物件,並向內深入,必要時還會偽裝成尚未撰寫的物件。

  • 應用程式的物件劃分

    • 正在測試的物件,稱之為受測物件
    • 其他所有的物件

    應當假設應用程式的其餘部分是被掩蓋的,因此在測試期間唯一的可用資訊只會來自於受測物件。

參考資料

  • Practical Object-Oriented Design in Ruby: An Agile Primer

上一篇
Day17 CH8組合物件(下)
下一篇
Day19 CH9 設計節省成本的測試(中)
系列文
入坑 RoR 必讀 - Ruby 物件導向設計實踐30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言